// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

`timescale 1 ns / 1 ps

`include "espi_header.iv"

module espi_pc_put_np_fsm
  (
   // Non-Posted Receive FIFO
   input logic         np_rxfifo_empty,
   output logic        np_rxfifo_get,
   input logic [7:0]   np_rxfifo_rdata,
   output logic        np_rxfifo_reset,

   // 
   input logic         rx_detect_crc_end,
   input logic         np_error_condition,
   
   // Completion Transmit FIFO
   output logic        pc_txfifo_req,
   input logic         pc_txfifo_gnt,
   output logic        pc_txfifo_put,
   output logic [7:0]  pc_txfifo_wdata,
   input logic         pc_txfifo_full,
   input logic         pc_txfifo_empty,
   output logic        pc_txfifo_avail,

   // Avalon Memory-Mapped Master to memory
   output logic        mem_req,
   input logic         mem_gnt,
   output logic [31:0] mem_address,
   output logic        mem_read,
   input logic [31:0]  mem_readdata,
   input logic         mem_waitrequest,

   // Globals
   input logic         clk,
   input logic         channel_reset_n
   );

    // Handle PUT_NP for CYCLETYPE MEM_READ_32
    enum {PUT_NP_IDLE,
          PUT_NP_START,
          PUT_NP_COMMAND,
          PUT_NP_CYCLETYPE,
          PUT_NP_TAG_LENGTH_HI,
          PUT_NP_LENGTH_LO,
          PUT_NP_ADDRESS_31_24,
          PUT_NP_ADDRESS_23_16,
          PUT_NP_ADDRESS_15_8,
          PUT_NP_ADDRESS_7_0,
          PUT_NP_CPL_CYCLETYPE,
          PUT_NP_CPL_TAG_LENGTH_HI,
          PUT_NP_CPL_LENGTH_LO,
          PUT_NP_CPL_LENGTH,
          PUT_NP_MEM_ACCESS,
          PUT_NP_READ,
          PUT_NP_READDATA_7_0,
          PUT_NP_READDATA_15_8,
          PUT_NP_READDATA_23_16,
          PUT_NP_READDATA_31_24,
          PUT_NP_FINISH} put_np_state;

    logic [3:0]  put_np_tag;
    logic [11:0] put_np_length;
    logic [1:0]  put_np_offset;
    logic [31:0] put_np_readdata;


    always @(posedge clk or negedge channel_reset_n) begin
        if (!channel_reset_n) begin
            mem_address     <= 0;
            mem_read        <= 0;
            mem_req         <= 0;
            np_rxfifo_get   <= 0;
            np_rxfifo_reset <= 0;
            pc_txfifo_avail <= 0;
            pc_txfifo_put   <= 0;
            pc_txfifo_req   <= 0;
            pc_txfifo_wdata <= 0;
            put_np_length   <= 0;
            put_np_offset   <= 0;
            put_np_readdata <= 0;
            put_np_tag      <= 0;
        end
        else begin
            pc_txfifo_put   <= 0;
            pc_txfifo_avail <= pc_txfifo_avail & ~pc_txfifo_empty;
            case (put_np_state)
              PUT_NP_IDLE: begin
                  np_rxfifo_get   <= 0;
                  np_rxfifo_reset <= 0;
                  mem_address  <= 0;
                  put_np_length   <= 0;
                  if (rx_detect_crc_end) begin
                      if (!np_error_condition) begin
                          if (!np_rxfifo_empty) begin
                              pc_txfifo_req <= 1'b1;
                              np_rxfifo_get <= 1'b1;
                              put_np_state  <= PUT_NP_START;
                          end
                      end
                  end
              end // case: PUT_NP_IDLE

              PUT_NP_START: begin
                  put_np_state <= PUT_NP_COMMAND;
              end

              PUT_NP_COMMAND: begin
                  if (np_rxfifo_rdata != `PUT_NP) begin
                      np_rxfifo_reset <= 1'b1;
                      put_np_state    <= PUT_NP_FINISH;
                  end
                  else begin
                      put_np_state <= PUT_NP_CYCLETYPE;
                  end
              end

              PUT_NP_CYCLETYPE: begin
                  if (np_rxfifo_rdata != `MEM_READ_32) begin
                      np_rxfifo_reset <= 1'b1;
                      put_np_state    <= PUT_NP_FINISH;
                  end
                  else begin
                      put_np_state <= PUT_NP_TAG_LENGTH_HI;
                  end
              end

              PUT_NP_TAG_LENGTH_HI: begin
                  put_np_tag          <= np_rxfifo_rdata[7:4];
                  put_np_length[11:8] <= np_rxfifo_rdata[3:0];
                  put_np_state        <= PUT_NP_LENGTH_LO;
              end

              PUT_NP_LENGTH_LO: begin
                  put_np_length[7:0] <= np_rxfifo_rdata;
                  put_np_state       <= PUT_NP_ADDRESS_31_24;
              end

              PUT_NP_ADDRESS_31_24: begin
                  mem_address[31:24] <= np_rxfifo_rdata;
                  put_np_state       <= PUT_NP_ADDRESS_23_16;
              end
                      
              PUT_NP_ADDRESS_23_16: begin
                  mem_address[23:16] <= np_rxfifo_rdata;
                  put_np_state       <= PUT_NP_ADDRESS_15_8;
              end
                      
              PUT_NP_ADDRESS_15_8: begin
                  mem_address[15:8] <= np_rxfifo_rdata;
                  put_np_state      <= PUT_NP_ADDRESS_7_0;
              end
                      
              PUT_NP_ADDRESS_7_0: begin
                  mem_address[7:0] <= np_rxfifo_rdata & 8'hfc;
                  put_np_offset    <= np_rxfifo_rdata[1:0];
                  if (pc_txfifo_gnt) begin
                      put_np_state <= PUT_NP_CPL_CYCLETYPE;
                  end
              end

              PUT_NP_CPL_CYCLETYPE: begin
                  pc_txfifo_put   <= 1'b1;
                  pc_txfifo_wdata <= `SUCCESSFUL_COMPLETE_DATA;
                  put_np_state    <= PUT_NP_CPL_TAG_LENGTH_HI;
              end

              PUT_NP_CPL_TAG_LENGTH_HI: begin
                  pc_txfifo_put   <= 1'b1;
                  pc_txfifo_wdata <= {put_np_tag, put_np_length[11:8]};
                  put_np_state    <= PUT_NP_CPL_LENGTH_LO;
              end

              PUT_NP_CPL_LENGTH_LO: begin
                  pc_txfifo_put   <= 1'b1;
                  pc_txfifo_wdata <= put_np_length[7:0];
                  mem_req         <= 1'b1;
                  put_np_state    <= PUT_NP_MEM_ACCESS;
              end

              PUT_NP_MEM_ACCESS: begin
                  if (mem_gnt) begin
                      mem_read     <= 1'b1;
                      put_np_state <= PUT_NP_READ;
                  end
              end

              PUT_NP_READ: begin
                  if (!mem_waitrequest) begin
                      mem_read        <= 0;
                      put_np_readdata <= mem_readdata;
                      put_np_length   <= put_np_length - 1'd1;
                      put_np_offset   <= 0;
                      case (put_np_offset)
                        0: begin
                            put_np_state <= PUT_NP_READDATA_7_0;
                        end

                        1: begin
                            put_np_state <= PUT_NP_READDATA_15_8;
                        end
                        
                        2: begin
                            put_np_state <= PUT_NP_READDATA_23_16;
                        end
                        
                        3: begin
                            put_np_state <= PUT_NP_READDATA_31_24;
                        end
                      endcase // case (put_np_offset)
                  end // if (!mem_waitrequest)
              end // case: PUT_NP_READ
                      
              PUT_NP_READDATA_7_0: begin
                  pc_txfifo_wdata <= put_np_readdata[7:0];
                  pc_txfifo_put   <= 1'b1;
                  if (put_np_length > 0) begin
                      put_np_length <= put_np_length - 1'd1;
                      put_np_state  <= PUT_NP_READDATA_15_8;
                  end
                  else begin
                      put_np_state <= PUT_NP_FINISH;
                  end
              end // case: PUT_NP_READDATA_7_0
                      
              PUT_NP_READDATA_15_8: begin
                  pc_txfifo_wdata <= put_np_readdata[15:8];
                  pc_txfifo_put   <= 1'b1;
                  if (put_np_length > 0) begin
                      put_np_length <= put_np_length - 1'd1;
                      put_np_state  <= PUT_NP_READDATA_23_16;
                  end
                  else begin
                      put_np_state <= PUT_NP_FINISH;
                  end
              end // case: PUT_NP_READDATA_15_8
                      
              PUT_NP_READDATA_23_16: begin
                  pc_txfifo_wdata <= put_np_readdata[23:16];
                  pc_txfifo_put   <= 1'b1;
                  if (put_np_length > 0) begin
                      put_np_length <= put_np_length - 1'd1;
                      put_np_state  <= PUT_NP_READDATA_31_24;
                  end
                  else begin
                      put_np_state <= PUT_NP_FINISH;
                  end
              end // case: PUT_NP_READDATA_23_16
                      
              PUT_NP_READDATA_31_24: begin
                  pc_txfifo_wdata <= put_np_readdata[31:24];
                  pc_txfifo_put   <= 1'b1;
                  if (put_np_length > 0) begin
                      mem_address  <= mem_address + 3'd4;
                      mem_read     <= 1'b1;
                      put_np_state <= PUT_NP_READ;
                  end
                  else begin
                      put_np_state <= PUT_NP_FINISH;
                  end
              end

              PUT_NP_FINISH: begin
                  pc_txfifo_avail <= ~pc_txfifo_empty;
                  pc_txfifo_req   <= 0;
                  mem_req         <= 0;
                  put_np_state    <= PUT_NP_IDLE;
              end
            endcase // case (put_np_state)
        end // else: !if(!channel_reset_n)
    end // always @ (posedge clk or negedge channel_reset_n)
    
endmodule // espi_pc_put_np_fsm
